using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Xml.Serialization;

namespace GraphAlgsPresentation.Core
{
    /// <summary>
    /// The class provides information of the graph vertex.
    /// </summary>
    [Serializable]
    public class LCVertex
    {
        private bool updated;
        private bool marked;
        private Point position = new Point(0, 0);
        private int r = 20;
        private string name = "Default";
        private string label = string.Empty;
        private Font font = new Font("Arial Black", 15);
        private Color fillColor = Color.CadetBlue;
        private Color textColor = Color.Black;
        private ContentAlignment _textAlign = ContentAlignment.BottomCenter;
        private Font _labelFont = new Font("Courier New", 15, FontStyle.Bold);

        #region Initialize

        /// <summary>
        /// Initializes a new instance of the <see cref="LCVertex"/> class.
        /// </summary>
        public LCVertex()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LCVertex"/> class.
        /// </summary>
        /// <param name="point">The point.</param>
        public LCVertex(Point point)
        {
            Position = point;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LCVertex"/> class.
        /// </summary>
        /// <param name="point">The point.</param>
        /// <param name="name">The name.</param>
        public LCVertex(Point point, string name)
        {
            Position = point;
            this.name = name;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LCVertex"/> is updated.
        /// </summary>
        /// <value><c>true</c> if updated; otherwise, <c>false</c>.</value>
        [Browsable(false), XmlIgnore]
        public bool Updated
        {
            get { return updated; }
            set { updated = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LCVertex"/> is marked.
        /// </summary>
        /// <value><c>true</c> if marked; otherwise, <c>false</c>.</value>
        [Browsable(false), XmlIgnore]
        public bool Marked
        {
            get { return marked; }
            set { marked = value; }
        }

        /// <summary>
        /// Gets or sets the position.
        /// </summary>
        /// <value>The position.</value>
        [Category("Vertex")]
        [Description("The position of the vertex.")]
        public Point Position
        {
            get { return position; }
            set { position = value; }
        }

        /// <summary>
        /// Gets or sets the radius.
        /// </summary>
        /// <value>The radius.</value>
        [Category("Vertex")]
        [Description("The radius of the vertex.")]
        public int Radius
        {
            get { return r; }
            set { r = value; }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        [Category("Vertex")]
        [DisplayName("(Name)")]
        [Description("The name of the vertex.")]
        public string Name
        {
            get { return name; }
            set
            {
                if (name == "Default") name = value;
            }
        }

        /// <summary>
        /// Gets or sets the label.
        /// </summary>
        /// <value>The label.</value>
        [Category("Vertex")]
        [Description("The label used to show more information about the vertex.")]
        public string Label
        {
            get { return label; }
            set { label = value; }
        }

        /// <summary>
        /// Gets or sets the font.
        /// </summary>
        /// <value>The font.</value>
        [XmlIgnore]
        [Category("Vertex")]
        [Description("The font used to display text for the vertex.")]
        public Font Font
        {
            get { return font; }
            set { font = value; }
        }

        /// <summary>
        /// Gets or sets the color of the fill.
        /// </summary>
        /// <value>The color of the fill.</value>
        [XmlIgnore]
        [Category("Vertex")]
        [DisplayName("Fill Color")]
        [Description("The color used to fill the vertex.")]
        public Color FillColor
        {
            get { return fillColor; }
            set { fillColor = value; }
        }

        /// <summary>
        /// Gets or sets the color of the text.
        /// </summary>
        /// <value>The color of the text.</value>
        [XmlIgnore]
        [Category("Vertex")]
        [DisplayName("Text Color")]
        [Description("The color used to display text for the vertex.")]
        public Color TextColor
        {
            get { return textColor; }
            set { textColor = value; }
        }

        /// <summary>
        /// Gets or sets the text align.
        /// </summary>
        /// <value>The text align.</value>
        [Category("Vertex")]
        [DisplayName("Text Align")]
        [Description("The alignment of the vertex's label.")]
        public ContentAlignment TextAlign
        {
            get { return _textAlign; }
            set { _textAlign = value; }
        }

        /// <summary>
        /// Gets or sets the label font.
        /// </summary>
        /// <value>The label font.</value>
        [XmlIgnore]
        [Category("Vertex")]
        [DisplayName("Label Font")]
        [Description("The font used to display the label of this vertex.")]
        public Font LabelFont
        {
            get { return _labelFont; }
            set { _labelFont = value; }
        }

        /// <summary>
        /// Gets or sets the XML label font.
        /// </summary>
        /// <value>The XML label font.</value>
        [Browsable(false)]
        public XmlFont XMLLabelFont
        {
            get { return new XmlFont(this.LabelFont); }
            set { this.LabelFont = value.ToFont(); }
        }

        /// <summary>
        /// Gets or sets the XML font.
        /// </summary>
        /// <value>The XML font.</value>
        [Browsable(false)]
        public XmlFont XMLFont
        {
            get { return new XmlFont(Font); }
            set { Font = value.ToFont(); }
        }

        /// <summary>
        /// Gets or sets the XML color of the fill.
        /// </summary>
        /// <value>The XML color of the fill.</value>
        [Browsable(false)]
        public string XMLFillColor
        {
            get { return XmlDataConverter.SerializeColor(FillColor); }
            set { FillColor = XmlDataConverter.DeserializeColor(value); }
        }

        /// <summary>
        /// Gets or sets the XML color of the text.
        /// </summary>
        /// <value>The XML color of the text.</value>
        [Browsable(false)]
        public string XMLTextColor
        {
            get { return XmlDataConverter.SerializeColor(TextColor); }
            set { TextColor = XmlDataConverter.DeserializeColor(value); }
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Checks the point(x, y) inside of the vertex.
        /// </summary>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <returns></returns>
        public bool CheckInside(int x, int y)
        {
            return (x - Position.X) * (x - Position.X) + (y - Position.Y) * (y - Position.Y) < Radius * Radius;
        }

        /// <summary>
        /// Changes the name.
        /// </summary>
        /// <param name="name">The name.</param>
        public void ChangeName(string name)
        {
            this.name = name;
        }

        /// <summary>
        /// Gets the intersectant point.
        /// </summary>
        /// <param name="p">The p.</param>
        /// <returns></returns>
        public PointF GetIntersectantPoint(Point p)
        {
            int x = p.X;
            int y = p.Y;
            float eps = 1;
            PointF point = new PointF();
            int height = Math.Max(Position.Y, y) + 1;
            float x1 = Position.X;
            float y1 = height - Position.Y;
            float x2 = x;
            float y2 = height - y;
            float dx = Math.Abs(x1 - x2);
            float dy = Math.Abs(y1 - y2);
            float d = (float)Math.Sqrt(dx * dx + dy * dy);
            float dx1 = (float)Radius * dx / d + eps;
            float dy1 = (float)Radius * dy / d + eps;
            if (x2 > x1) point.X = x1 + dx1;
            else point.X = x1 - dx1;
            if (y2 > y1) point.Y = y1 + dy1;
            else point.Y = y1 - dy1;
            point.Y = height - point.Y;
            return point;
        }

        #endregion

        #region Graphic methods

        /// <summary>
        /// Draws on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void Draw(Graphics g)
        {
            DrawVertex(g);
            DrawName(g);
            DrawLabel(g);
        }

        /// <summary>
        /// Draws the vertex on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        private void DrawVertex(Graphics g)
        {
            Rectangle rc = new Rectangle(Position.X - Radius, Position.Y - Radius, Radius * 2, Radius * 2);
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(rc);

            PathGradientBrush brush = new PathGradientBrush(path);
            brush.CenterPoint = new Point(Position.X - Radius / 2, Position.Y - Radius / 2);
            brush.CenterColor = Color.White;
            brush.SurroundColors = new Color[1] { FillColor };
            g.FillEllipse(brush, rc);
            brush.Dispose();

            Pen pen = new Pen(FillColor, 3);            
            g.DrawEllipse(pen, rc);
            pen.Dispose();
        }

        /// <summary>
        /// Draws the name on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        private void DrawName(Graphics g)
        {
            Rectangle rc = new Rectangle(Position.X - Radius, Position.Y - Radius, Radius * 2, Radius * 2);
            Brush brush = new SolidBrush(TextColor);
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;
            g.DrawString(Name, Font, brush, rc, format);
            brush.Dispose();
        }

        /// <summary>
        /// Gets the string format.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <param name="position">The position.</param>
        private void GetStringFormat(out StringFormat format, out Point position)
        {
            position = new Point();
            format = new StringFormat();
            string TextAlignName = this.TextAlign.ToString();
            #region Vertical alignment
            if (TextAlignName.Contains("Top"))
            {
                format.LineAlignment = StringAlignment.Far;
                position.Y = this.Position.Y - this.Radius;
            }
            else if (TextAlignName.Contains("Middle"))
            {
                format.LineAlignment = StringAlignment.Center;
                position.Y = this.Position.Y;
            }
            else if (TextAlignName.Contains("Bottom"))
            {
                format.LineAlignment = StringAlignment.Near;
                position.Y = this.Position.Y + this.Radius;
            }
            #endregion
            #region Horizontal alignment
		            if (TextAlignName.Contains("Left"))
            {
                format.Alignment = StringAlignment.Far;
                position.X = this.Position.X - this.Radius;
            }
            else if (TextAlignName.Contains("Center"))
            {
                format.Alignment = StringAlignment.Center;
                position.X = this.Position.X;
            }
            else if (TextAlignName.Contains("Right"))
            {
                format.Alignment = StringAlignment.Near;
                position.X = this.Position.X + this.Radius;
            }
	        #endregion       
        }

        /// <summary>
        /// Draws the label on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        private void DrawLabel(Graphics g)
        {
            if (Label.Length == 0) return;
            Brush brush = new SolidBrush(TextColor);
            StringFormat format;
            Point position;
            GetStringFormat(out format, out position);
            g.DrawString(Label, LabelFont, brush, position, format);
            brush.Dispose();
        }

        /// <summary>
        /// Draws the mark on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        public void DrawMark(Graphics g)
        {
            int length = 5;
            int X = Position.X;
            int Y = Position.Y;
            Pen pen = new Pen(Color.Black);
            g.DrawLine(pen, X - Radius, Y - Radius, X - Radius + length, Y - Radius);
            g.DrawLine(pen, X - Radius, Y - Radius, X - Radius, Y - Radius + length);
            g.DrawLine(pen, X - Radius, Y + Radius, X - Radius + length, Y + Radius);
            g.DrawLine(pen, X - Radius, Y + Radius, X - Radius, Y + Radius - length);
            g.DrawLine(pen, X + Radius, Y - Radius, X + Radius - length, Y - Radius);
            g.DrawLine(pen, X + Radius, Y - Radius, X + Radius, Y - Radius + length);
            g.DrawLine(pen, X + Radius, Y + Radius, X + Radius - length, Y + Radius);
            g.DrawLine(pen, X + Radius, Y + Radius, X + Radius, Y + Radius - length);
            pen.Dispose();
        }

        /// <summary>
        /// Draws the vertex with alpha on the specified graphics.
        /// </summary>
        /// <param name="g">The graphics.</param>
        /// <param name="alpha">The alpha.</param>
        public void DrawAlpha(Graphics g, int alpha)
        {
            Rectangle rc = new Rectangle(Position.X - Radius, Position.Y - Radius, Radius * 2, Radius * 2);
            GraphicsPath path = new GraphicsPath();
            path.AddEllipse(rc);

            PathGradientBrush brush = new PathGradientBrush(path);
            brush.CenterPoint = new Point(Position.X - Radius / 2, Position.Y - Radius / 2);
            brush.CenterColor = Color.White;
            Color alphaColor = Color.FromArgb(alpha, FillColor);
            brush.SurroundColors = new Color[1] { alphaColor };
            g.FillEllipse(brush, rc);
            brush.Dispose();

            Pen pen = new Pen(alphaColor, 3);
            g.DrawEllipse(pen, rc);
            pen.Dispose();

            DrawName(g);
            DrawLabel(g);
        }

        #endregion

        #region Serialization

        /// <summary>
        /// Serializes the specified output.
        /// </summary>
        /// <param name="output">The output.</param>
        public void Serialize(TextWriter output)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(LCVertex));
            serializer.Serialize(output, this);
        }

        /// <summary>
        /// Deserializes the specified input.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <returns></returns>
        public static LCVertex Deserialize(TextReader input)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(LCVertex));
            return serializer.Deserialize(input) as LCVertex;
        }

        #endregion
    }
}
